﻿using System;
using System.Collections.Generic;
using System.Text;
using jade.core.behaviours;
using MarketSimulator.Utils;
using MarketSimulator.Utils.Attributes;
using jade.core;
using jade.lang.acl;
using MarketSimulator.Utils.Communication;
using MarketSimulator.Utils.Maps;
using System.Threading;

namespace MarketSimulator.UI.Assistant
{
    /// <summary>
    /// GEt the best price when you want to buy stuff
    /// </summary>
    public class GetBestPriceBuyBehavior : OneShotBehaviour
    {
        /// <summary>
        /// Name of the product
        /// </summary>
        string ProductName;

        /// <summary>
        /// Quantity Required
        /// </summary>
        int Quantity;

        /// <summary>
        /// City where the products are needed
        /// </summary>
        City CityOfDestination;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="productName"></param>
        /// <param name="quantity"></param>
        /// <param name="cityOfDestination"></param>
        /// <param name="Agent"></param>
        public GetBestPriceBuyBehavior(string productName, int quantity, City cityOfDestination, AssistantAgent Agent)
        {
            ProductName = productName;
            Quantity = quantity;
            CityOfDestination = cityOfDestination;
        }

        public override void action()
        {
            var sellers = YellowPages.FindService(SellerAttributes.SellerServiceName, null, this.myAgent, 2000);

            AskSellersFroPrice(ProductName, sellers);
            Thread.Sleep(500);
            var prices = ReadSellersPrices();

            (myAgent as AssistantAgent).WriteToChat(Environment.NewLine + "Fund sellers: ");
            foreach (var price in prices)
            {
                (myAgent as AssistantAgent).WriteToChat(string.Format("[{0}] : price = {1}; quantity = {2}", price.Key, price.Value.Price, price.Value.Quantity));
            }

            (myAgent as AssistantAgent).WriteToChat(Environment.NewLine + "------------------------" + Environment.NewLine);
            var storageAgents = YellowPages.FindService(StorageAttributes.StorageServiceName, null, this.myAgent, 2000);
            AskStorageAgents(storageAgents);
            Thread.Sleep(500);
            var storageOffers = ReadStorageOffers();

            (myAgent as AssistantAgent).WriteToChat(Environment.NewLine + "Fund storage: ");
            foreach (var price in storageOffers)
            {
                (myAgent as AssistantAgent).WriteToChat(string.Format("[{0}] : price = {1}", price.Key, price.Value));
            }
        }
        
        #region Sellers Conversation
        
        /// <summary>
        /// Ask Prices
        /// </summary>
        /// <param name="product"></param>
        /// <param name="sellers"></param>
        private void AskSellersFroPrice(string product, IList<AID> sellers)
        {
            foreach (var seller in sellers)
            {
                ACLMessage newMessage = new ACLMessage();
                newMessage.addReceiver(seller);
                newMessage.setConversationId(Messages.CONVERSATION_ASK_PRICE);
                newMessage.setContent(product);
                myAgent.send(newMessage);
            }
        }

        /// <summary>
        /// Read Prices
        /// </summary>
        private Dictionary<string, ProductOffer> ReadSellersPrices()
        {
            Dictionary<string, ProductOffer> prices = new Dictionary<string, ProductOffer>();
            ACLMessage receivedMessage;
            MessageTemplate receivePongTemplate = MessageTemplate.MatchConversationId(Messages.CONVERSATION_SAY_PRICE);

            do
            {
                receivedMessage = myAgent.receive(receivePongTemplate);
                if (receivedMessage != null)
                {
                    int newPrice = 0;
                    int newQuantity = 0;
                    string[] parts = receivedMessage.getContent().Split('|');
                    if (int.TryParse(parts[0], out newPrice) && int.TryParse(parts[1], out newQuantity))
                    {
                        var buyOffer = new ProductOffer()
                        {
                            Name = receivedMessage.getSender().getLocalName(),
                            Price = newPrice,
                            Quantity = newQuantity
                        };

                        prices.Add(receivedMessage.getSender().getLocalName(), buyOffer);
                    }
                }

            } while (receivedMessage != null);

            return prices;
        }

        #endregion

        #region Depositers Conversation

        /// <summary>
        /// Ask Prices
        /// </summary>
        /// <param name="product"></param>
        /// <param name="sellers"></param>
        private void AskStorageAgents(IList<AID> sellers)
        {
            string content = string.Format("{0}|{1}", CityOfDestination.Id, Quantity);

            foreach (var seller in sellers)
            {
                ACLMessage newMessage = new ACLMessage();
                newMessage.addReceiver(seller);
                newMessage.setConversationId(Messages.CONVERSATION_ASK_DEPOSIT_OFFER);
                newMessage.setContent(content);
                myAgent.send(newMessage);
            }
        }

        private Dictionary<string, double> ReadStorageOffers()
        {
            Dictionary<string, double> prices = new Dictionary<string, double>();
            ACLMessage receivedMessage;
            MessageTemplate receivePongTemplate = MessageTemplate.MatchConversationId(Messages.CONVERSATION_SAY_DEPOSIT_OFFER);

            do
            {
                receivedMessage = myAgent.receive(receivePongTemplate);
                if (receivedMessage != null)
                {
                    double price = 0;

                    if (double.TryParse(receivedMessage.getContent(), out price))
                    {
                        prices.Add(receivedMessage.getSender().getLocalName(), price);
                    }
                }

            } while (receivedMessage != null);

            return prices;
        }

        #endregion
    }
}
